package com.fyp.ptma.controllers;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fyp.ptma.dao.service.iservice.IDownloaderService;
import com.fyp.ptma.gcm.Constants;
import com.fyp.ptma.gcm.Message;
import com.fyp.ptma.gcm.MulticastResult;
import com.fyp.ptma.gcm.Result;
import com.fyp.ptma.model.Downloader;

@Controller
@RequestMapping(value = "/secure")
public class GcmController extends AbstractController {
	private static final int MULTICAST_SIZE = 1000;
	private static final Executor threadPool = Executors.newFixedThreadPool(5);

	@Autowired
	private IDownloaderService downloaderService;

	@RequestMapping(value = "notifyDevices.html", method = RequestMethod.POST)
	@ResponseBody
	public String sendNotification(Model model, HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		String[] ids = request.getParameterValues("deviceIds");
		Authentication auth = SecurityContextHolder.getContext()
				.getAuthentication();
		String email = auth.getName();

	 

		List<Downloader> devices = downloaderService.getDownloadersByDeviceIds(
				Arrays.asList(ids), email);
		String application = "TO DO: Real app name";

		if (!devices.isEmpty()) {

			// NOTE: check below is for demonstration purposes; a real
			// application
			// could always send a multicast, even for just one recipient
			if (devices.size() == 1) {
				// send a single message using plain post
				String registrationId = devices.get(0).getGoogleRegId();
				Message message = new Message.Builder().addData("application",
						application).build();

				Result res = ServletContextListener.SENDER.send(message,
						registrationId, 5);
				res.toString();
			} else {
				// send a multicast message using JSON
				// must split in chunks of 1000 devices (GCM limit)
				int total = devices.size();
				Set<String> partialDevices = new HashSet<String>(total);
				int counter = 0;

				for (Downloader device : devices) {
					counter++;
					partialDevices.add(device.getGoogleRegId());
					int partialSize = partialDevices.size();
					if (partialSize == MULTICAST_SIZE || counter == total) {
						asyncSend(partialDevices, application);
						partialDevices.clear();

					}

				}
			}
		}

		return "<b>Notification To Downloader Sent Successfully";
	}

	private void asyncSend(Set<String> partialDevices,
			final String applicationName) {
		// make a copy
		final List<String> devices = new ArrayList<String>(partialDevices);
		threadPool.execute(new Runnable() {

			public void run() {
				Message message = new Message.Builder().addData("application",
						applicationName).build();
				MulticastResult multicastResult;
				try {
					multicastResult = ServletContextListener.SENDER.send(
							message, devices, 5);
				} catch (IOException e) {

					return;
				}
				List<Result> results = multicastResult.getResults();
				// analyze the results
				for (int i = 0; i < devices.size(); i++) {
					String regId = devices.get(i);
					Result result = results.get(i);
					String messageId = result.getMessageId();
					if (messageId != null) {
						// logger.fine("Succesfully sent message to device: " +
						// regId +
						// "; messageId = " + messageId);
						String canonicalRegId = result
								.getCanonicalRegistrationId();
						if (canonicalRegId != null) {
							// same device has more than on registration id:
							// update it
							// logger.info("canonicalRegId " + canonicalRegId);
							// Datastore.updateRegistration(regId,
							// canonicalRegId);
						}
					} else {
						String error = result.getErrorCodeName();
						if (error.equals(Constants.ERROR_NOT_REGISTERED)) {
							// application has been removed from device -
							// unregister it

							// Datastore.unregister(regId);
						} else {
							// logger.severe("Error sending message to " + regId
							// + ": " + error);
						}
					}
				}
			}
		});
	}

}
